package real.evaluation.formula;

import java.util.ArrayList;
import java.util.List;

import real.evaluation.Evaluator;
import real.evaluation.Formula;
import real.evaluation.RealEvaluationException;
import real.lang.RealLangException;
import real.lang.literal.RealBoolean;

public class CompositeFormulaExpression extends Formula {

	private LogicalJunction junction;

	private ArrayList<Formula> formulas = new ArrayList<Formula>();

	public RealBoolean ProperEvaluate(Evaluator eval) throws RealLangException {
		if (junction == LogicalJunction.NOT && formulas.size() != 1)
			throw new RealEvaluationException(
					"There should only one formula at NOT logical juction.");	
		
		if (junction == LogicalJunction.NOT)
		{
			RealBoolean result = this.formulas.get(0).Evaluate(eval);
			result.setValue(!result.getValue());
			return result;
		}
		
		if (formulas.size() < 2)
			throw new RealEvaluationException(
					"There should be two or more formulas at OR/AND logical juction.");
		
		RealBoolean result = this.formulas.get(0).Evaluate(eval);
		if (this.junction == LogicalJunction.OR) {
			for (int i = 0; i < this.formulas.size(); i++) {
				RealBoolean next = this.formulas.get(i).Evaluate(eval);
				result.setValue(result.getValue() || next.getValue());
				if (result.getValue())
					return result;
			}
		} else if (this.junction == LogicalJunction.AND) {
			for (int i = 0; i < this.formulas.size(); i++) {
				RealBoolean next = this.formulas.get(i).Evaluate(eval);
				result.setValue(result.getValue() && next.getValue());
				if (!result.getValue())
					return result;
			}
		} else {
			throw new RealEvaluationException("Nonimplemented logical juction.");
		}
		return result;
	}
	
	/**
	 * @param juction
	 *            the junction to set
	 */
	public void setJunction(LogicalJunction junction) {
		this.junction = junction;
	}

	/**
	 * @return the junction
	 */
	public LogicalJunction getJunction() {
		return junction;
	}

	/**
	 * @param formulas
	 *            the predicates to set
	 */
	public void setFormulas(List<Formula> formulas) {
		this.formulas.clear();
		this.formulas.addAll(formulas);
	}

	/**
	 * @param formulas
	 *            the predicates to set
	 */
	public void addFormulas(List<Formula> formulas) {		
		this.formulas.addAll(formulas);
	}
	
	/**
	 * @param formulas
	 *            the predicates to set
	 */
	public void addFormula(Formula formula) {		
		this.formulas.add(formula);
	}
	
	/**
	 * @return the predicates
	 */
	public ArrayList<Formula> getFormulas() {
		return formulas;
	}
	
	public CompositeFormulaExpression(LogicalJunction junction, Formula formula) {
		this.junction = junction;
		this.formulas.add(formula);
	}
	public CompositeFormulaExpression(LogicalJunction junction, List<? extends Formula> formulas) {
		this.junction = junction;
		this.formulas.addAll(formulas);
	}

}
